package com.du.cloud.service.gateway.filter;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.du.cloud.common.base.constant.RedisKeyConstants;
import com.du.cloud.common.base.constant.SecurityConstants;
import com.du.cloud.common.base.exception.ValidateCodeException;
import com.du.cloud.common.base.utils.Result;
import com.du.cloud.common.base.utils.WebUtils;
import com.du.cloud.common.cache.utils.RedisUtil;
import com.du.cloud.service.gateway.config.IgnoreClientConfiguration;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.DefaultServerRequest;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Desc: 登录验证过滤
 * 验证码校验
 * Author: dushuang
 * Date: 2020/5/30
 **/
@Slf4j
@AllArgsConstructor
public class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory {
    private final IgnoreClientConfiguration ignoreClient;
    private final ObjectMapper objectMapper;

    private Map<String, Object> decodeBody(String body) {
        log.info(body);
        return Arrays.stream(body.split("&")).map(s -> s.split("="))
                .collect(Collectors.toMap(arr -> arr[0], arr -> arr[1]));
    }

    private String encodeBody(Map<String, Object> map) {
        return map.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
    }

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();


            // POST获取form数据
            MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
            String method = exchange.getRequest().getMethodValue();
            // new body map
            Map<String, Object> newBodyMap = new HashMap<>();
            if ("POST".equals(method)) {
                ServerRequest serverRequest = new DefaultServerRequest(exchange);
                Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
                    if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)) {
                        // origin body map
                        //这是你post请求获取到的参数
                        Map<String, Object> bodyMap = decodeBody(body);
                        Arrays.stream(new String[]{"client_id", "client_secret", "grant_type"}).forEach(k -> {
                            log.info("===={}===", bodyMap.get(k));
                            newBodyMap.put(k, bodyMap.get(k));
                        });
                        //重新放回
                        return Mono.just(encodeBody(bodyMap));
                    }
                    return Mono.empty();
                });
                log.info("{}", newBodyMap);
                BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
                HttpHeaders headers = new HttpHeaders();
                headers.putAll(exchange.getRequest().getHeaders());

                // the new content type will be computed by bodyInserter
                // and then set in the request decorator
                headers.remove(HttpHeaders.CONTENT_LENGTH);

                CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
                return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
                    ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {

                        public HttpHeaders getHeaders() {
                            long contentLength = headers.getContentLength();
                            HttpHeaders httpHeaders = new HttpHeaders();
                            httpHeaders.putAll(super.getHeaders());
                            if (contentLength > 0) {
                                httpHeaders.setContentLength(contentLength);
                            } else {
                                httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                            }
                            return httpHeaders;
                        }

                        public Flux<DataBuffer> getBody() {
                            return outputMessage.getBody();
                        }
                    };
                    return chain.filter(exchange.mutate().request(decorator).build());
                }));
            } else {
                return chain.filter(exchange);
            }


            // 不是登录请求，直接向下执行
//            if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath()
//                    , SecurityConstants.OAUTH_TOKEN_URL)) {
//                return chain.filter(exchange);
//            }
//
//            // 刷新token，直接向下执行
//            String grantType = request.getQueryParams().getFirst("grant_type");
//            if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)) {
//                return chain.filter(exchange);
//            }
//
//            // 终端设置不校验， 直接向下执行
//            try {
//                String[] clientInfos = WebUtils.getClient(request);
//                if (ignoreClient.getClients().contains(clientInfos[0])) {
//                    return chain.filter(exchange);
//                }
//
//                //校验验证码
//                checkCode(request);
//            } catch (Exception e) {
//                ServerHttpResponse response = exchange.getResponse();
//                response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED);
//                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
//
//                final String errMsg = e.getMessage();
//                return response.writeWith(Mono.create(monoSink -> {
//                    try {
//                        byte[] bytes = objectMapper.writeValueAsBytes(Result.fail(errMsg));
//                        DataBuffer dataBuffer = response.bufferFactory().wrap(bytes);
//
//                        monoSink.success(dataBuffer);
//                    } catch (JsonProcessingException jsonProcessingException) {
//                        log.error("对象输出异常", jsonProcessingException);
//                        monoSink.error(jsonProcessingException);
//                    }
//                }));
//            }
//
//            return chain.filter(exchange);
        };
    }


    /**
     * 检查code
     *
     * @param request
     */
    @SneakyThrows
    private void checkCode(ServerHttpRequest request) {
        String verifyCode = request.getQueryParams().getFirst("verifyCode");

        if (StrUtil.isBlank(verifyCode)) {
            throw new ValidateCodeException("验证码不能为空");
        }

        String imgToken = request.getQueryParams().getFirst("imgToken");

        String key = RedisKeyConstants.CAPTCHA_CODE.concat(imgToken);

        String code = RedisUtil.get(key);

        if (StrUtil.isEmpty(code)) {
            throw new ValidateCodeException("验证码不合法");
        }

        if (!StrUtil.equals(verifyCode, code)) {
            RedisUtil.remove(key);
            throw new ValidateCodeException("验证码不合法");
        }

        RedisUtil.remove(key);
    }
}
